

#include "packet_frame_operate.h"
#include "input_output_context.h"
#include "media_stream.h"
#include "codec_context.h"
#include "base_util.h"
#include <unistd.h>
#include <errno.h>

void ReadPacketDataToMessageQueue(InputFormatContext * input_format_ctx, AVPacket *packet, PacketMessageQueue &packet_message_queue)
{
	while (av_read_frame(input_format_ctx->AvFormatContext(), packet) >= 0)
	{
		AVPacket * pkt = av_packet_alloc();
		av_packet_move_ref(pkt, packet);
		packet_message_queue.PushPacket(&pkt);
		av_packet_unref(packet);
	}
	packet_message_queue.SetQueueEof();
}

void ReadPacketDataCallback(InputFormatContext * input_format_ctx, std::function<bool(InputFormatContext *, AVPacket * )> && function_callback)
{
    AVPacket * packet = av_packet_alloc();
    while (av_read_frame(input_format_ctx->AvFormatContext(), packet) >= 0)
    {
        if (!function_callback(input_format_ctx, packet))
        {
            av_packet_unref(packet);
            break;
        }
        av_packet_unref(packet);
    }
    av_packet_free(&packet);
}

void ReadPacketDataWithCallback(InputFormatContext * input_format_ctx, AVPacket * packet, std::function<bool(InputFormatContext *, AVPacket * )> && function_callback)
{
	bool is_need_free_packet = 0;
	if (packet == nullptr)
	{
		packet = av_packet_alloc();
		is_need_free_packet = 1;
	}
	while (av_read_frame(input_format_ctx->AvFormatContext(), packet) >= 0)
	{
		if (!function_callback(input_format_ctx, packet))
		{
			av_packet_unref(packet);
			break;			
		}
		av_packet_unref(packet);
	}
	if (is_need_free_packet)
	{
		av_packet_free(&packet);
	}
}

bool DecodePacketDataToMessageQueue(std::shared_ptr<DecoderContext> codec_ctx, AVPacket * packet, FrameMessageQueue  & frame_message_queue)
{
	if (codec_ctx)
	{
		return DecodePacketDataToMessageQueue(codec_ctx.get(), packet, frame_message_queue);
	}
	return false;
}

bool DecodePacketDataToMessageQueue(DecoderContext * decode_ctx, AVPacket *packet, FrameMessageQueue &frame_message_queue)
{
	int result = avcodec_send_packet(decode_ctx->AvCodecCtx(), packet);
	if (result < 0)
	{
		PrintLog(result);
		return false;
	}
	while (result >= 0)
	{
		AVFrame * frame = av_frame_alloc();
		result = avcodec_receive_frame(decode_ctx->AvCodecCtx(), frame);
		if (result < 0)
		{
			av_frame_free(&frame);
			if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
			{
				break;
			}
			return false;
		}
		else
		{
			// if (decode_ctx->GetHwMethod() != -1)
			// {
			// 	decode_ctx->ProcessHwFrameStatus();
			// }
			frame_message_queue.PushFrame(&frame);
		}
	}
	return true;
}

bool DecodePacketDataToQueue(std::shared_ptr<DecoderContext> decode_ctx, AVPacket * packet, FrameMessageQueue & frame_message_queue)
{
	int result = avcodec_send_packet(decode_ctx->AvCodecCtx(), packet);
	if (result < 0)
	{
		PrintLog(result);
		return false;
	}
	while (result >= 0)
	{
		AVFrame * frame = av_frame_alloc();
		result = avcodec_receive_frame(decode_ctx->AvCodecCtx(), frame);
		if (result < 0)
		{
			if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
			{
				break;
			}
			av_frame_free(&frame);
			return false;
		}
		else
		{
			int method = decode_ctx->GetHwMethod();
			if (method & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX)
			{
				decode_ctx->ProcessHwFrameStatus();
			}
			frame_message_queue.PushFrame(&frame);
		}
	}
	return true;
}

void ProcessPacketDataFromMessageQueue(PacketMessageQueue &message_queue, std::function<void (AVPacket *)> &&process_func, bool eof_break)
{
	AVPacket * packet = nullptr;
	while (true)
	{
		int result = message_queue.PopPacketNonBlocking(&packet);
		if (result < 0)
		{
			if (result == AVERROR_EOF)
			{
				break;
			}
			if (!eof_break)
			{
				break;
			}
			usleep(1000);
			continue;
		}
		if (process_func)
		{
			process_func(packet);
		}
		av_packet_unref(packet);
		av_packet_free(&packet);
	}
}

void ProcessFrameDataFromMessageQueue(FrameMessageQueue & message_queue, std::function<void(AVFrame  * frame)>  && process_func, bool eof_break)
{
	AVFrame * frame = nullptr;
	while (true)
	{
		int result = message_queue.PopFrameNonBlocking(&frame);
		if (result < 0)
		{
			if (result == AVERROR_EOF)
			{
				break;
			}
			if (!eof_break)
			{
				break;
			}
			continue;
		}
		if (process_func)
		{
			process_func(frame);
		}
		av_frame_unref(frame);
		av_frame_free(&frame);
	}
}

bool EncodeFrameDataToMessageQueue(std::shared_ptr<EncoderContext> codec_ctx, AVFrame * frame, PacketMessageQueue & message_queue)
{
	int result = avcodec_send_frame(codec_ctx->AvCodecCtx(), frame);
	if (result < 0)
	{
		PrintLog(result);
		return false;
	}
	while (result >= 0)
	{
		AVPacket * packet = av_packet_alloc();
		result = avcodec_receive_packet(codec_ctx->AvCodecCtx(), packet);
		if (result < 0)
		{
			av_packet_free(&packet);
			if (result == AVERROR_EOF || result == AVERROR(EAGAIN))
			{
				break;
			}
			return false;
		}
		message_queue.PushPacket(&packet);
	}
	return true;
}

bool FilterFrame(FilterGraphContext * filter_graph, AVFrame * frame, FrameMessageQueue & message_queue)
{
    bool filter_result = true;
    int result = av_buffersrc_add_frame_flags(filter_graph->GetFilterBufferCtx(), frame, AV_BUFFERSRC_FLAG_KEEP_REF);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    else
    {
        while (true)
        {
            AVFrame * fme = av_frame_alloc();
            result = av_buffersink_get_frame(filter_graph->GetFilterBufferSinkCtx(), fme);
            if (result < 0)
            {
                filter_result = false;
                if (result == AVERROR(EAGAIN) || result == AVERROR_EOF)
                {
                    filter_result = true;
                }
                av_frame_free(&fme);
                break;
            }
            else
            {
                message_queue.PushFrame(&fme);
            }
        }
    }
    return filter_result;
}
